import React, { useState, useMemo, useEffect } from "react";
import { Common, Validate } from "UtilsCommon";
import PropertyItem from "./propertyItem";
import Base from './base';

const setValue = (dataList, setDataList) => {
    dataList = dataList || [];
    setDataList(dataList);
};

const getPrimaryKey = (property) => {
    return property.entity ? property.entity.primaryKey : property.primaryKey || 'id';
};

const add = (data, property, dataList, setDataList) => {
    const primaryKey = getPrimaryKey(property);
    data[primaryKey] = data[primaryKey] || Common.createGuid();

    let blExists = false;
    const list = [];

    for (let i = 0; i < dataList.length; i++) {
        if (dataList[i][primaryKey] === data[primaryKey] || (property.keyName && dataList[i][property.keyName] === data[property.keyName])) blExists = true;
        else list.push(dataList[i]);
    }

    if (blExists) return;
    else list.push(data);

    setValue(list, setDataList);
};

const addList = (list, property, dataList, setDataList) => {
    if (!list || list.length === 0) return;
    const primaryKey = getPrimaryKey(property);
    list.forEach(m => {
        m[primaryKey] = m[primaryKey] || Common.createGuid();
    });
    const list2 = dataList.concat(list);
    setValue(list2, setDataList);
};

const update = (data, property, dataList, setDataList) => {
    const primaryKey = getPrimaryKey(property);
    const id = data[primaryKey];
    const editData = Common.arrayFirst(dataList, (f) => Common.isEquals(f[primaryKey], id, true));
    if (editData !== null) {
        for (const key in data) editData[key] = data[key];

        setValue(dataList.map(m => m), setDataList);
    }
};

const remove = (id, property, dataList, setDataList) => {
    const primaryKey = getPrimaryKey(property);
    setValue(dataList.filter(f => !Common.isEquals(f[primaryKey], id, true)), setDataList);
};

const validateValue = (validateName, v, p) => {
    if (!Validate[validateName]) return "";

    let msg = Validate[validateName](v, p.label);
    if (msg === true) msg = "";
    else if (p.validateTipMessage) msg = p.validateTipMessage;

    return msg;
};

const judgeNullable = (value, property, rowViews) => {
    const { nullTipMessage } = property;

    if (value.length === 0 && !property.isRowNullable) return nullTipMessage;

    let msg = ""; let v = null; let p = null;

    const primaryKey = getPrimaryKey(property);

    for (let j = 0; j < value.length; j++) {
        const view = rowViews[value[j][primaryKey]];
        if (!view) continue;
        for (let i = 0; i < view.properties.length; i++) {
            p = view.properties[i];
            if (!p.getValue || !p.isEdit || p.isVisible === false) continue;
            v = p.getValue();
            if (!p.isNullable && p.type === "Select" && Common.isNullOrEmpty(v)) {
                msg = p.nullTipMessage || "请选择" + p.label + "！";
                break;
            }
            else if (!p.isNullable && Common.isNullOrEmpty(v)) {
                msg = p.nullTipMessage || p.label + "不能为空！";
                break;
            }
            else if (!Common.isNullOrEmpty(v) && p.validateNames) {
                for (let i = 0; i < p.validateNames.length; i++) {
                    msg = validateValue(p.validateNames[i], v, p);
                    if (Common.isNullOrEmpty(msg)) break;
                };

                if (!Common.isNullOrEmpty(msg)) break;
            }
        }

        if (!Common.isNullOrEmpty(msg)) break;
    }

    return msg;
};

const setProperties = (properties, property, id, index, data) => {
    if (!properties) return;

    properties.forEach(p => {
        const id2 = p.id + id;

        const propertyName = p.propertyName || p.name;
        p.value = data[propertyName];
        p.id = id2;
        p.dataId = id;
        p.dataIndex = index;
        p.data = data;
        p.isBind = true;
        if (data[`${p.name}_isVisible`] === false) p.isVisible = false;

        if (p.name === property.deletePropertyName) {
            p.eventActionName = 'deleteRow_' + property.id;
        }

        if (p.properties) setProperties(p.properties, property, id, index, data);
    });
};

const rendDataItem = (data, index, length, property, pageId, pageAxis, rowViews) => {
    const primaryKey = getPrimaryKey(property);
    const id = data[primaryKey] || Common.createGuid();
    data[primaryKey] = id;
    let view = rowViews[id];
    if (!view) {
        view = Common.clone(property.properties[0]); rowViews[id] = view;
        view.id = Common.createGuid();
    }

    const fn = pageAxis.getFunction(property.fnSetProperties);
    if (fn) fn({ data, view, index, length });

    setProperties(view.properties, property, id, index, data);

    return <PropertyItem property={view} key={view.id} view={property} pageId={pageId} />;
};

const rendDataItemList = (dataList, property, pageId, pageAxis, rowProperties) => {
    property.initDataList && property.initDataList(dataList, property);
    return dataList.map((m, i) => rendDataItem(m, i, dataList.length, property, pageId, pageAxis, rowProperties));
};

const getDataRowViews = (rowViews, dataList, property) => {
    const primaryKey = getPrimaryKey(property);
    const list = [];
    dataList.forEach(d => {
        const id = d[primaryKey];
        if (rowViews[id]) list.push(rowViews[id]);
    });
    return list;
};

export default React.memo((props) => {
    const { property, pageAxis, pageId } = Base.getProps(props);
    const [isVisible, setIsVisible] = useState(property.isVisible !== false);
    const [dataList, setDataList] = useState(property.value || property.defaultValue || []);

    const rowViews = useMemo(() => ({}), []);

    useEffect(() => {
        if (property.setDataLoad) property.setDataLoad(dataList, property);
    }, [dataList, property]);

    useEffect(() => {
        if (property.addPropertyName) {
            const add = pageAxis.getProperty(property.addPropertyName);
            add.eventActionName = 'addRow_' + property.id;
        }
    }, [property, pageAxis]);

    pageAxis['addRow_' + property.id] = () => {
        let defaultRowData = property.defaultRowData || {};
        if (pageAxis.getDefaultRowData) defaultRowData = pageAxis.getDefaultRowData(property, dataList);
        add({ ...defaultRowData }, property, dataList, setDataList);
    };

    pageAxis['deleteRow_' + property.id] = (a) => {
        remove(a.property.dataId, property, dataList, setDataList);
    };

    property.setIsVisible = (v) => { property.isVisible = v; setIsVisible(v); };
    property.add = (v) => add(v, property, dataList, setDataList);
    property.addList = (v) => addList(v, property, dataList, setDataList);
    property.update = (v) => update(v, property, dataList, setDataList);
    property.remove = (v) => remove(v, property, dataList, setDataList);
    property.setValue = (v) => setValue(v, setDataList);
    property.getValue = () => dataList;
    property.refresh = () => setDataList(dataList.map(m => m));
    property.judgeNullable = (v) => judgeNullable(v, property, rowViews);
    property.getDataRowViews = () => getDataRowViews(rowViews, dataList, property);

    if (property.isBind && property.data) property.data[property.name] = dataList;

    if (!isVisible || dataList.length === 0) return null;

    return (
        <div style={Base.setStyle(property.style)} className={property.className}>
            {rendDataItemList(dataList, property, pageId, pageAxis, rowViews)}
        </div>
    );
});